;
(function (w, doc) {
    class Dep {
        constructor() {
            this.subS = []
        }
        addSub(watcher) {
            this.subS.push(watcher)
        }
        notify() {
            this.subS.forEach(watcher => watcher.update())
        }
    }
    class Watcher {
        constructor(vm, key, cb) {
            this.vm = vm;
            this.key = key;
            this.cb = cb;
            Dep.target = this;
            key.split('.').reduce((obj, k) => obj[k], vm);
            Dep.target = null;
        }
        update() {
            const value = this.key.split('.').reduce((obj, k) => obj[k], this.vm);
            this.cb(value)
        }
    }
    class Watch {
        constructor() {
            this.watchData = []
        }
        addWatch(vm, watch, key) {
            this.addWatchProps({
                fn: watch[key].bind(vm),
                key
            })
        }
        addWatchProps(props) {
            this.watchData.push(props);
        }
        invoke(key, newVal, oldVal) {
            this.watchData.forEach(v => {
                if (v.key === key) {
                    v.fn(newVal, oldVal)
                }
            })
        }
    }
    class Computed {
        constructor() {
            this.computedData = []
        }
        addComputed(vm, computed, key, subsrciber) {
            const desc = Object.getOwnPropertyDescriptor(computed, key);


            const descFn = desc.value;

            const value = descFn.call(vm);
            const dep = this.collectDep(descFn);
            const get = descFn.bind(vm)
            this.addComputedProps({
                key,
                dep,
                get,
                value
            })
            const dataItem = this.computedData.find(v => v.key === key);
            Object.defineProperty(vm, key, {
                get() {
                    Dep.target && subsrciber.addSub(Dep.target);
                    return dataItem.value
                }
            })
        }
        addComputedProps(props) {
            this.computedData.push(props)
        }
        collectDep(fn) {
            const matched = fn.toString().match(/this\.?(\S+)/g);
            return matched.map(v => v.split('.')[1]);
        }
        update(key, cb) {
            this.computedData.forEach(v => {
                const dep = v.dep;
                const _key = dep.find(k => k === key);
                if (_key) {
                    v.value = v.get();
                }
            })
            cb && cb()
        }
    }
    w.Dep = Dep
    w.Watcher = Watcher;
    w.Watch = Watch;
    w.Computed = Computed
})(window, document)